#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
	unsigned int flag;
	unsigned int addr;
	unsigned int value;
	unsigned int pc;

	unsigned int addrd;
	unsigned int valued;
	unsigned int pcd;
} type_last;
type_last* type_hash = 0;

#define HASH_SIZE 1024

main() {
	FILE* f = fopen( "compressedplus.bin", "rb");
	FILE* c = fopen( "compressed.bin", "wb" );
	
	type_hash = (type_last*)calloc(HASH_SIZE, sizeof(type_last));
	memset(type_hash, 0, sizeof(type_last)*HASH_SIZE);
	while ( !feof(f) ) {
		unsigned int flags;
		fread(&flags, sizeof(unsigned int), 1, f);
		unsigned short matchcount = flags & 0xFFFF;
		unsigned char regChange = (flags>>16)&1;
		unsigned char memAcc = (flags>>17)&3;
		unsigned char size = (flags>>19)&3;
		char packsize = size;
		if ( size == 3 ) size = 4;
		unsigned char target = (flags>>24)&31;
		unsigned char done = (flags>>31)&1;
		unsigned char longMatch = (flags>>30)&1;
		if ( done ) break;
			
		unsigned int idx = (regChange%2) + ((memAcc%4)<<1) + ((packsize%4)<<3) + ((target%32)<<5);
		type_last* hashv = &type_hash[idx%HASH_SIZE]; // mod by HASH_SIZE just in case
			
		unsigned int r_addr = 0;
		unsigned int r_value = 0;
		unsigned int r_pc = 0;
		unsigned int lflags = (regChange<<16) + (memAcc<<17)+((size)<<19)
			+(target<<24);
		fwrite(&lflags, sizeof(unsigned int), 1, c);
			
		if ( (flags>> 21)&1 ) {
			r_addr = hashv->addr + hashv->addrd;
		} else if ( memAcc ) {
			fread(&r_addr, sizeof(unsigned int), 1, f);
		}
		if ( memAcc ) {
			fwrite(&r_addr, sizeof(unsigned int), 1, c);
		}


		if ( (flags>>22)&1 ) {
			r_value = hashv->value + hashv->valued;
		} else if ( regChange ) {
			fread(&r_value, sizeof(unsigned int), 1, f);
		}
		if ( regChange ) {
			fwrite(&r_value, sizeof(unsigned int), 1,c );
		}

		if ( (flags>>23)&1 ) {
			r_pc = hashv->pc + hashv->pcd;
		} else {
			fread(&r_pc, sizeof(unsigned int), 1, f);
		}
		fwrite(&r_pc, sizeof(unsigned int), 1, c);
		int addrd = (int)r_addr - hashv->addr;
		int valued = (int)r_value - hashv->value;
		int pcd = (int)r_pc - hashv->pc;
		
		hashv->addr = r_addr;
		hashv->value = r_value;
		hashv->pc = r_pc;

		hashv->addrd = addrd;
		hashv->valued = valued;
		hashv->pcd = pcd;

		
	}
}
